﻿using System;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Xml.Serialization;
using StreamingClient.Player;

namespace StreamingClient.StreamManagment
{
    public class VlcAccess : IServerAccess
    {
        const string StreamName = "stream";
        
        public event EventHandler<StreamingEventArgs> StreamingStarted;
        public event EventHandler<ErrorEventArgs> CommunicationFailed;

        private StreamingSettings _settings;

        public VlcAccess(StreamingSettings settings)
        {
            _settings = settings;
        }

        private bool ServerAvailable()
        {
            return true;
        }


        public void StartStreaming(string source)
        {
            Uri uri;
            if (!Uri.TryCreate(source, UriKind.RelativeOrAbsolute, out uri))
            {
                if (CommunicationFailed != null)
                {
                    CommunicationFailed(this, new ErrorEventArgs(ErrorCode.StartStream, new Exception("No valid Uri: " + source)));
                }
                return;
            }
            //var cmd = source.Replace(@"\", @"\\");
            //cmd = cmd.Replace('/', '\\');
            var cmd = uri.ToString();
            var vlmcmd =
    string.Format(
        "new {10} broadcast enabled input \"{11}\" output #transcode{{vcodec={0},vb={1},fps={2},width={3},height={4},venc=x264{{profile=baseline,level=1}},acodec={5},ab={6},channels={7},samplerate={8}}}:http{{dst=:{9}/{10}.ts}}",
        _settings.VCodec, _settings.VBitrate, _settings.VFps, _settings.VWidth, _settings.VHeight,
        _settings.ACodec,
        _settings.ABitrate, _settings.AChannels, _settings.ASampleRate, _settings.ControlPort, StreamName, cmd);


            SendCommandVLM(vlmcmd, PlayStream);
        }

        private void PlayStream()
        {
            SendCommandVLM(string.Format("control {0} play", StreamName), NotifyClient);
        }

        private void NotifyClient()
        {
            if (StreamingStarted != null)
            {
                var target = string.Format("http://{0}:{1}/{2}.ts", _settings.Host, _settings.ControlPort, StreamName);
                StreamingStarted(this, new StreamingEventArgs(target));
            }
        }

        public void StopStreaming()
        {
            SendCommandVLM(string.Format("control {0} stop", StreamName), DelStream);
        }

        public void Seek(double pos)
        {
            SendCommandVLM(string.Format("control {0} seek {1}", StreamName, pos), null);
        }

        public void UpdatePos(Action<double> callback)
        {
            var url = string.Format("http://{0}:{1}/requests/vlm.xml", _settings.Host, _settings.ControlPort);
            WebRequest webRequest = WebRequest.Create(url);
            webRequest.BeginGetResponse(GetStateCompleted, new SendCommandVLMState() { request = webRequest, callback = callback});
        }

        private void GetStateCompleted(IAsyncResult ar)
        {
            var state = (SendCommandVLMState)ar.AsyncState;
            double pos = 0;
            try
            {
                var response = state.request.EndGetResponse(ar);
                var stream=response.GetResponseStream();
                var sr = new StreamReader(stream);
                var txt = sr.ReadToEnd();
                var startidx = txt.IndexOf("position=\"");
                var endidx = txt.IndexOf("\" ", startidx);
                var posstr = txt.Substring(startidx+10, endidx - startidx-10);
                double.TryParse(posstr,NumberStyles.Float,CultureInfo.InvariantCulture.NumberFormat, out pos);
                //var ser = new XmlSerializer(typeof(vlm));
                //var vlmstate = (vlm)ser.Deserialize(stream);
                //var bc=vlmstate.Items.FirstOrDefault(i => i.name == streamName);
                //if (bc != null)
                //{
                //    double.TryParse(bc.instances[0][0].position, out pos);
                //}
            }
            catch (Exception ex)
            { }
            if (state.callback != null)
            {
                ((Action<double>)state.callback).Invoke(pos);
            }

        }


        private void DelStream()
        {
            SendCommandVLM(string.Format("del {0}", StreamName), null);
        }

        private void GetVLMStreams()
        {
            ///requests/vlm.xml
        }

        private class SendCommandVLMState
        {
            public WebRequest request;
            public Delegate callback;
        }

        private void SendCommandVLM(string command, Action callback)
        {
            if (ServerAvailable())
            {
                command = Uri.EscapeDataString(command);

                string text = string.Format("http://{0}:{1}/requests/vlm_cmd.xml?command={2}", _settings.Host, _settings.ControlPort, command);
                WebRequest webRequest = WebRequest.Create(text);
                webRequest.BeginGetResponse(SendCommandVLMCompleted, new SendCommandVLMState(){callback = callback,request = webRequest});
            }
            else
            {
                if (CommunicationFailed!=null)
                {
                    CommunicationFailed(this, new ErrorEventArgs(ErrorCode.VLCNotAvailable, null));
                }
            }
        }

        private void SendCommandVLMCompleted(IAsyncResult ar)
        {
            var state = (SendCommandVLMState) ar.AsyncState;
            try
            {
                var response = state.request.EndGetResponse(ar);
            }
            catch (Exception ex)
            {
                if (CommunicationFailed != null)
                {
                    CommunicationFailed(this, new ErrorEventArgs(ErrorCode.VLCNotAvailable, ex));
                }

            }
            if (state.callback != null)
            {
                state.callback.DynamicInvoke();
            }
        }
    }
}
